﻿using System.Collections.Generic;
using System.Linq;
using SymbolIntegration.core.Expressions;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core
{
    public class IntegrateSurfaceData
    {
        public Expression MainExpression;
        public string IntegratedVariable;
        public Dictionary<string, KeyValuePair<Expression, Expression>> OtherVariablesForIntegrateWithBounds;

        public Expression SurfaceExpression;
    }

    static public class ExpressionFacade
    {
        public static Expression Parse(string expressionText)
        {
            return ParseUtils.ParseExpression(expressionText);
        }

        public static Expression Integrate(Expression expression, string variable)
        {
            return IntegrationHelper.Integrate(expression, variable).Normalize();
        }

        public static Expression Differentiate(Expression expression, string variable)
        {
            return expression.Differentiate(variable).Normalize();
        }

        public static Expression DifferentiateByVector(Expression expression, Dictionary<string, Expression> vectorCoordanates)
        {
            Expression result = new ConstantExpression(0);
            foreach (var vectorCoordanate in vectorCoordanates)
            {
                result = result + expression.Differentiate(vectorCoordanate.Key)*vectorCoordanate.Value;
            }

            result = result.Normalize();
            return result;
        }

        public static Expression Substitude(Expression expressionTo, Expression subtitude, string insteadVariable)
        {
            return expressionTo.Substitute(new Dictionary<string, Expression> { { insteadVariable, subtitude } }).Normalize();
        }

        public static Expression IntegrateWithBounds(Expression expressionTo, string variable, Expression upBound, Expression lowBound)
        {
            var integrated = Integrate(expressionTo, variable);
            var result = Substitude(integrated, upBound, variable) - Substitude(integrated, lowBound, variable);
            result = result.Normalize();
            return result;
        }

        public static Expression IntegrateBySurface(IntegrateSurfaceData surfaceData)
        {
            var result = surfaceData.MainExpression;
            var vectorCoordinates = surfaceData.OtherVariablesForIntegrateWithBounds.Select(pair =>
                surfaceData.SurfaceExpression.Differentiate(pair.Key));
            Expression additionalMult = new ConstantExpression(1);
            additionalMult = vectorCoordinates.Aggregate(additionalMult, 
                (current, vectorCoordinate) => current + vectorCoordinate*vectorCoordinate);

            additionalMult = new PowerExpression(additionalMult, new ConstantExpression(0.5)).Normalize();

            result = result*additionalMult;
            result = Substitude(result, surfaceData.SurfaceExpression, surfaceData.IntegratedVariable);

            foreach (var other in surfaceData.OtherVariablesForIntegrateWithBounds)
            {
                result = IntegrateWithBounds(result, other.Key, other.Value.Key, other.Value.Value);
            }

            return result;
        }
    }
}
